#!/usr/sbin/rsct/perl5/bin/perl
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 1999,2002 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
#"@(#)91   1.21   src/rsct/registry/cli/bin/lssr.perl, srcli, rsct_rpyxh, rpyxht1f3 2/22/01 16:25:23"
######################################################################
#                                                                    #
# Module: lssr                                                       #
#                                                                    #
# Purpose:                                                           #
#   lssr - Lists (displays) the contents of a directory.             #
#                                                                    #
# Syntax:                                                            #
#   lssr [-h][-C|l|d|D Delimiter][-F][-R][-TV] [Directory...]        #
#                                                                    #
# Flags:                                                             #
#   -h  Help. Writes this command's usage statement to stdout.       #
#   -C|l|d|D Delimiter                                               #
#       Output format flags. -C Sorts output vertically in a multi   #
#       column format. This is the default method when output is to a#
#       terminal. -l (lowercase l) forces output into a              #
#       one-entry-per-line format. This is the default method when   #
#       output is not directed to a terminal. The -d flag uses stream#
#       output format with a colon-separated list.  -D uses the      #
#       supplied Delimiter to separate fields in the output if this  #
#       flag is used.                                                #
#   -F  Puts a / (slash) after each file name if the entry is a      #
#       directory. Puts a ? (question mark) after each entry name,   #
#       if the entry is of unknown type. (This is considered a severe#
#       error.)                                                      #
#   -R  Lists all subdirectories recursively.                        #
#   -T Trace. Writes this command's trace messages to stderr.        #
#   -V Verbose. Writes this command's verbose messages to stderr.    #
#                                                                    #
# Operands:                                                          #
#   Directory     The name of the directory in the system registry   #
#                 that we want to list. Zero or more directory       #
#                 operands can be specified.  A relative or absolute #
#                 directory can be specified.                        #
#                                                                    #
# Description:                                                       #
#   The lssr command writes to standard output the contents of each  #
#   specified Directory operand. If no directory operand is specified#
#   the lssr command displays the contents of the current directory. #
#   The current directory may be set by using the CT_SR_HOME         #
#   environment variable, if CT_SR_HOME is unset the current         #
#   directory defaults to the System Registry root (/).  By default, #
#   the absolute path of each entry is displayed. If a relative      #
#   Directory operand is specified, then the paths displayed are     #
#   relative to this directory.                                      #
#                                                                    #
#   There are three main ways to format the output:                  #
#       - List one entry per line.                                   #
#       - List entries in multiple columns by specifying the -C flag.#
#         The -C flag is the default format when output is to a tty. #
#         The lssr command displays single column output file if     #
#         directory or table name is too long.                       #
#       -  List entries in a colon-separated series by specifying the#
#          -d flag.                                                  #
#       - List entries in a user specified delimiter-separated series#
#         by specifying the - D delimiter flag.                      #
#                                                                    #
#   To determine the number of character positions in the output     #
#   line, the lssr command uses the COLUMNS environment variable. If #
#   this variable is not set, it uses the /usr/bin/termdef -c        #
#   command.   If the lssr command cannot determine the number of    #
#   character positions by either of these methods, it uses a default#
#   value of 80.                                                     #
#                                                                    #
#   If `?' is displayed at the end of an entry when the -F flag is   #
#   specified, this is considered a severe Registry error. Contact   #
#   your nearest IBM support personnel in this case.                 #
#                                                                    #
# Exit Values:                                                       #
#   0  SR_CLI_SUCCESS        Command completed successfully.         #
#   1  SR_CLI_REGISTRY_ERROR Command terminated due to an underlying #
#                            System Registry error.                  #
#   2  SR_CLI_ERROR          Command terminated due to an underlying #
#                            error in the command script.            #
#   3  SR_CLI_BAD_OPERAND    Command terminated due to user          #
#                            specifying a bad operand.               #
#   4  SR_CLI_BAD_FLAG       Command terminated due to user          #
#                            specifying an invalid flag.             #
#   5  SR_CLI_USER_ERROR     Command terminated due to a user error. #
#                            For example specifying an undefined     #
#                            directory to be listed.                 #
#                                                                    #
# Examples:                                                          #
#   lssr -F /Cluster /Node                                           #
#   lssr                                                             #
#   lssr -D :: /IBM                                                  #
#   lssr -RF / - will print entire Registry tree, marking tables     #
#                and directories                                     #
#                                                                    #
#--------------------------------------------------------------------#
# Inputs:                                                            #
#   /usr/sbin/rsct/msgmaps/srcli.lssr.map - message mapping          #
#                                                                    #
# Outputs:                                                           #
#   stdout - display of the directory list.                          #
#   stderr - any error message.                                      #
#                                                                    #
# External Ref:                                                      #
#   Commands: $LSMSG                                                 #
#   Modules:  SR_cli_utils.pm                                        #
#   Extensions:  CT::SR.pm CT::SRrc.pm                               #
#   Perl library routines: Getopt::Std                               #
#                                                                    #
# Tab Settings:                                                      #
#   4 and tabs should be expanded to spaces before saving this file. #
#   in vi:  (:set ts=4  and   :%!expand -4)                          #
#                                                                    #
# Change Activity:                                                   #
#   000929 HGJ 38317: Initial delivery.                              #
#                                                                    #
######################################################################

#--------------------------------------------------------------------#
# General Program Flow/Logic:                                        #
#                                                                    #
# A. Parse command line flags and operands.                          #
# B. If no directory operand specified establish what the current    #
#    directory is using CT_SR_HOME or if that's not set use the      #
#    system registry root directory (/) as the current directory.    #
# C. Initialize a Session with the System Registry.                  #
# D. Loop through the list of directories (operands),                #
#    request each directory list from the system registry,           #
#    then display each list with either single column or multiple    #
#    column formatting.                                              #
# E. Terminate the Session with the system registry and any other    #
#    necessary cleanup.                                              #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# Included Libraries and Extensions                                  #
#--------------------------------------------------------------------#
use lib "/usr/sbin/rsct/pm";
use locale;
use Getopt::Std; 

use Env qw(CT_SR_HOME);
use Env qw(COLUMNS);

use CT_cli_utils qw(printIMsg
                    printEMsg
);

use CT::SRrc;
use CT::SR;
use CT::SR qw(:sr_entry_t);
use SR_cli_rc qw(   SR_CLI_REGISTRY_ERROR 
                    SR_CLI_BAD_FLAG 
                    SR_CLI_USER_ERROR
);
use SR_cli_utils qw($DEFAULT_GLOBAL_MOUNT_POINT
                    init_session 
                    term_session 
                    isRelative 
                    printCEMsg
                    error_exit
); 


#--------------------------------------------------------------------#
# Global Variables                                                   #
#--------------------------------------------------------------------#
# Constants
$TRUE           = 1;
$FALSE          = 0;

# Globals and default values
$Verbose        = $FALSE;              # verbose turned off
$Trace          = $FALSE;              # trace turned off

$Opt_Append_Type = $FALSE;             # see -F
$Opt_Line_Mode  = $TRUE;               # see -l (line mode) 
$Opt_Columns    = $FALSE;              # see -C (column mode) 
$Opt_Recursive  = $FALSE;              # see -R (recursive)
$Opt_Delimiter  = $FALSE;              # see -d | D delimiter 

# Messaging variables
$PROGNAME       = "lssr";              # Program Name for messages
$MSGCAT         = "srcli.cat";         # msg catalogue for this cmd.
$CTDIR          = "/usr/sbin/rsct";    # Cluster directory path
$CTBINDIR       = "$CTDIR/bin";        # Cluster Bin directory path
$LSMSG          = "$CTBINDIR/ctdspmsg"; # display message rtn.
$ENV{'MSGMAPPATH'} = "$CTDIR/msgmaps";  # Msg maps used by $LSMSG

%Cleanup = ();                         # Hash of items to cleanup
                                       # {Session} $session to term

#--------------------------------------------------------------------#
# Variables                                                          #
#--------------------------------------------------------------------#
my $Dir_list       = CT::SR::entry_metadata_t->new();
my $Tree_handle    = "";
my @Directory      = ();        
my $Set_work_dir   = $FALSE;
my $rc             = 0;
my $badrc          = 0;
my $no_ops         = $FALSE;          # To flag use of './' in lists
my $Real_directory = "";
my $Use_relative   = $FALSE;


#--------------------------------------------------------------------#
# Main Code                                                          #
#--------------------------------------------------------------------#

# Parse the command line, exit if there are errors 
($rc, @Directory) = parse_cmd_line();
($rc == 0) || error_exit($rc);

# If no directory operands then list the current directory 
# If environment variable $CT_SR_HOME is set that is the current 
# directory. Otherwise $SR_ROOT ("/") is the current directory
# TODO: define constant $SR_ROOT somewhere
if (!@Directory) {                      # No directory operands
    @Directory = $CT_SR_HOME ? $CT_SR_HOME : "/";  
    $no_ops = $TRUE;
}       
else {
    # Determine if the current working directory needs to be
    # set in the System Registry - if any of the directories listed
    # are relative, then it needs to be
    foreach (@Directory) {
        if (isRelative($_)) {$Set_work_dir = $TRUE;}
        $Set_work_dir and last;
    }
}


# Establish a session
# CT::SR::open_tree, CT::SR::mount_directory, 
# CT::SR::change_current_directory
# Uses Env qw(CT_SR_HOME, CT_SR_NUM_RETRIES, CT_SR_TIMEOUT) if they 
# are set. Assuming isRelative returns true for an empty directory 
# this way for lssr, when no directory is specified, we want to 
# display the current directory or CT_SR_HOME.  So we need 
# change_current_directory to be called for CT_SR_HOME.
($rc, $Tree_handle) = init_session($Set_work_dir);
($rc == 0) || error_exit(SR_CLI_REGISTRY_ERROR);

# Add the Tree handle to the Cleanup hash
$Cleanup{Session} = $Tree_handle;


# List the directories 
$badrc = 0;
foreach $directory (@Directory) {
    $Verbose && printIMsg("IMsglssrListingDir", $directory);

    # If the directory is an absolute directory we must prefix it with
    # the mount point.
    if (isRelative($directory)) { 
        $Real_directory = $directory;   

        # Use_relative is needed to flag conversion of the absolute 
        # entries the Registry returns to relative path names.
        $Use_relative = $TRUE;
    }
    else {
        # The other case that $Use_relative is going to be needed
        # is when the only directory given was the current working dir,
        # in which case there is 1 entry on @Directory, and it will
        # equal $CT_SR_HOME - this was flagged with the $no_ops flag

        if ($no_ops) {
            $Use_relative = $TRUE;
        }

        $Real_directory = $DEFAULT_GLOBAL_MOUNT_POINT . $directory;
    } 

    $Trace && 
       print "Calling CT::SR::get_directory__list($Real_directory)\n";
    $rc = CT::SR::get_directory_list($Tree_handle, $Real_directory,
         $Opt_Recursive, $Dir_list); 
    $Trace && print "CT::SR::get_directory__list return code: $rc\n";

    $rc = error_check("sr_get_directory_list", $rc, $directory);
    
    # If there was a real Registry error vs a parameter error 
    # then exit, otherwise try to create the other directories.
    ($rc == SR_CLI_REGISTRY_ERROR) && error_exit($rc);

    # Save the first bad return code 
    if ($rc != 0 && $badrc == 0) { $badrc = $rc; }

    # Call subroutine to set up the display if the directory was ok.
    if (!$rc) {
        $rc = create_display(\@Directory, $directory, $Use_relative, 
                        $Dir_list);

        if ($rc == SR_CLI_REGISTRY_ERROR) {
            # Just quit, this is serious
            printEMsg("EMsglssrQuestionMarkError");
            error_exit(SR_CLI_REGISTRY_ERROR);
        }
    }

    # Provided there were entries in this directory free the space,
    # but do not terminate if there is an internal
    # error, just display an error message and continue.
    if ($Dir_list->get_array_count > 0) {

        $Trace && 
            print "Calling CT::SR::free_directory_list($Real_directory)\n";
        $rc = CT::SR::free_directory_list($Dir_list);
        $Trace && 
            print "CT::SR::free_directory_list return code: $rc\n";

        $rc = error_check("sr_free_directory_list", $rc, $directory);
        # Save the first bad return code 
        if ($rc != 0 && $badrc == 0) { $badrc = $rc; }

    }
}

($badrc == 0) || error_exit($badrc);

# Cleanup 
$rc = term_session($Tree_handle, $DEFAULT_GLOBAL_MOUNT_POINT);

exit $rc;

#--------------------------------------------------------------------#
# End Main Code                                                      #
#--------------------------------------------------------------------#


#--------------------------------------------------------------------#
# parse_cmd_line - Parse the command line for options and operands.  #
#   Set appropriate global variables as outlined below, make sure we #
#   have a valid combination of arguments / options.                 #
#   TODO: all exit -1 should go away when full support coded.        #
#                                                                    #
# Return:                                                            #
#   $rc   0                  Command line parsed fine, no problem.   #
#         SR_CLI_BAD_FLAG    Command line contained a bad flag.      #
#   @directories             Array of directories to list.           #
#                                                                    #
# Global Variables Modified:                                         #
#   $Opt_Line_Mode     in/out   On input, the default, TRUE - set to #
#                               On output, set to FALSE if -c,-d,-D  #
#                               flag specified and -l is not.        #
#                               Print one entry perl line.           #
#   $Opt_Delimitter    output   Delimiter String (-d | -D delimitter)#
#                               When not False, display delimitter   #
#                               separated output using this del str. #
#   $Opt_Columns       output   True (-C) multi column output.       #
#   $Opt_Recursive     output   True (-R) list underlying directories#
#   $Opt_Append_Type   output   True (-F) append slash to directory  #
#                               names.                               #
#   $Trace             output   True (-T) turn Trace mode on.        #
#   $Verbose           output   True (-V) turn Verbose mode on.      #
#--------------------------------------------------------------------#
sub parse_cmd_line 
{
my(@original_argv) = @ARGV;
my(@directory, $goodrc, $badrc);
my %opts = ();

# Process the command line
if (!&getopts('hFCldD:RTV',\%opts)) {   # Gather options; if errors
    print_usage();                      # display proper usage
    return SR_CLI_BAD_FLAG;             # return nogood - bad flag 
}

# If no operands in lssr that is OK, it just means display the 
# current directory 

# Get the arguments
@directory = @ARGV;                     # user specified directory names

# See which options/flags were used...
if (defined $opts{h}) {                 # -h, help request  
    print_usage();                      # print usage statement
    exit(0);                            # all done with good return!
}

if (defined $opts{F}) {                 # -F put a / after directories
    $Opt_Append_Type = $TRUE;           # put a ? after unknown type
}

# Flag precedence: l>D>d>C No messages printed, just the highest
# order flag is chosen

if (defined $opts{l}) {                 # -l one entry per line
    $Opt_Line_Mode = $TRUE;
}

if (defined $opts{d}) {                 # -d colon delimited stream 
    $Opt_Delimiter = ":";               # output (except recursive)  
    if (!defined $opts{l}) {
        $Opt_Line_Mode = $FALSE;
    }
}

if (defined $opts{D}) {                 # -D delimited output
    $Opt_Delimiter = $opts{D};          # (except recursive case)
    if (!defined $opts{l}) {
        $Opt_Line_Mode = $FALSE;
    }
}

if (defined $opts{C}) {                 # -C multi column
    $Opt_Columns = $TRUE;
    if (!defined $opts{l}) {
        $Opt_Line_Mode = $FALSE;
    }
}

if ($Opt_Line_Mode) {
    $Opt_Delimitter = $FALSE;
    $Opt_Columns = $FALSE;
}
if ($Opt_Delimitter) {
    $Opt_Columns = $FALSE;
}

if (defined $opts{R}) {                 # -R recursive
    $Opt_Recursive = $TRUE;
}

if (defined $opts{T}) {                 # -T trace
    $Trace = $TRUE;
}

if (defined $opts{V}) {                 # -V verbose 
    $Verbose = $TRUE;
}

return(0, @directory);                  # success
}   # end parse_cmd_line


#--------------------------------------------------------------------#
# error_check:                                                       #
#   Checks the return code from the SR function.  If an error is     #
#   detected appropriate error messages will be displayed and        #
#   SR CLI return code set.                                          #
#                                                                    #
# Parameters:                                                        #
#   $sr_function  - Name of the SR function that was called and      #
#                   whose error code we are checking.                #
#   $sr_rc        - SR function return code.                         #
#   $directory    - Name of the directory trying to display.         #
#                                                                    #
# Return values:                                                     #
#   $rc           - SR C API rc converted to a SR CLI rc.            #
#                                                                    #
# Global References:                                                 #
#   ENV $CT_SR_HOME                                                  #
#--------------------------------------------------------------------#
sub error_check
{
my ($sr_function, $sr_rc, $directory) = @_;
my $rc = 0;

if ($sr_rc != 0) {
    if ($sr_rc == SR_NO_PERMISSION) {  
        printEMsg("EMsglssrPermissionError", $directory);
        $rc = SR_CLI_USER_ERROR;    
    }
    elsif ($sr_rc == SR_NO_DIRECTORY) {
        if ($CT_SR_HOME && isRelative($directory)) {
            $directory = $CT_SR_HOME.'/'.$directory; 
        }
        printEMsg("EMsglssrDirPathError", $directory);
        $rc = SR_CLI_USER_ERROR;
    }
    elsif ($sr_function eq "sr_get_directory_list") {
        printEMsg("EMsglssrListDirError", $directory);
        printCEMsg("EMsgSRcliSRCommandFailure", $sr_function, $sr_rc);
        $rc = SR_CLI_REGISTRY_ERROR;
    }
    elsif ($sr_function eq "sr_free_directory_list") {
        printEMsg("EMsglssrFreeDirListError", $directory);
        printCEMsg("EMsgSRcliSRCommandFailure", $sr_function, $sr_rc);
        $rc = SR_CLI_REGISTRY_ERROR;
    }
    else { 
        $rc = SR_CLI_REGISTRY_ERROR;
    }
}

return ($rc);
}   # end error_check


#--------------------------------------------------------------------#
# calc_col_widths:                                                   #
#   Calculates column widths based on the data from an input array,  #
#   plus number of rows and columns the data is supposed to be put   #
#   in.                                                              #
#                                                                    #
# Parameters:                                                        #
#   $rows - number of rows to work calculation with                  #
#   $cols - number of columns to work calculation with               #
#   @output_list - data to be displayed in the columns               #
#                                                                    #
# Return values:                                                     #
#   @column_widths - array containing the widths of each column      #
#--------------------------------------------------------------------#
sub calc_col_widths 
{
my ($rows, $cols, @output_list) = @_;

# Set up local variables
my $i = 0;
my $j = 0;
my $maxlen = 0;
my $len = 0;
my @column_widths = ();

# Go through each column and calculate the maximum width.
# There is no maximum column with. Large entries (ie, longer than
# the screen width) are wrapped in ls, so they are here as well.
for ($i = 0; $i < $cols; $i++) {
    for ($j = 0; $j<$rows; $j++) {
        $len = length($output_list[($i*$rows) + $j]);
        if ($len > $maxlen) {
             $maxlen = $len;
        }
    }
    $column_widths[$i] = $maxlen;
    $maxlen = 0;
}

return @column_widths;
}   # end calc_col_widths


#--------------------------------------------------------------------#
# calc_columns:                                                      #
#   Recursive subroutine that calculates the number of columns to be #
#   shown to the screen depending on the data to be listed and the   #
#   screen width.                                                    #
#                                                                    #
# Parameters:                                                        #
#   $column_length  - assumed column length                          #
#   $column_count   - column count at start of the iteration         #
#   $screen_width   - absolute value as calculated in calling routine#
#   $iter           - iteration number                               #
#   @data           - information to be sorted into columns          #
#                                                                    #
# Return values:                                                     #
#   $local_rc       - 0 : success  1: gone two iterations too far    #
#                      4 : go back one more iteration for appropriate#
#                      return values                                 #
#   $rows/$column_length - number of entries in a column             #
#   $cols/$column_count  - number of columns                         #
#                                                                    #
#--------------------------------------------------------------------#
sub calc_columns 
{
my ($column_length, $column_count, $screen_width, $iter, @data) = @_;

# Make sure $iter is handed in as 0 from the outside (first) call
$iter++;

# Set up local variables
my $local_rc = 1;
my $rows = 0;
my $cols = 0;

# Calculate column widths based on the values passed in
my @widths = calc_col_widths($column_length, $column_count, @data);

my $remainder = $screen_width;

if ($#widths == 0) {
    $remainder = $remainder - $widths[0] - 1;
}
else {
    foreach(@widths) {
        $remainder = $remainder - $_ - 3;
    }
    # to make up for the fact that the last entry has a \n, but
    # no spacing after it
    # why does this have to be 1, not 2?
    $remainder = $remainder + 2;
}

#print "remainder: $remainder\n";

# See what value of $remainder there is to flag whether we're done
# or if we have to go back one step to the correct values (ie, the
# iteration has gone too far)

if ($remainder < 0) {
    return(1, $column_length, $column_count);
}
elsif ($remainder == 0) {
    return(0, $column_length, $column_count);
}
elsif ($column_length == 1) {
    return(0, $column_length, $column_count);
}
else {

    # If code gets to here, then the spacing between columns has
    # to be accounted for in the remainder of the screen width
    # so add the '2' back on

    $remainder = $remainder - 2;

    # Calculate column count and lengths for one more column
    # before recursively calling
    $column_count++;
    $column_length = ($#data+1)/$column_count;

    if ($column_length =~ /\./) {

        # Implies there is a remainder that must be chopped.
        # So take off everything to the right of the decimal and
        # increment the column length
        $column_length =~ s/^(.*)\..*$/$1/;
        $column_length++;

    }

    while ($local_rc != 0) {

        ($local_rc, $rows, $cols) = calc_columns($column_length,
              $column_count, $screen_width, $iter, @data);

        if (($local_rc == 1)) {
            # The column iteration went too far - so the
            # values from this level are the valid ones.
            if ($iter == 1) { return (0, $#data+1, 1); }
            else { return(4, $rows, $cols);}
        }
        elsif( $local_rc == 4) {
            return(0, $column_length, $column_count);
        }

    }


} # end else

# Get out if the values have been found
return($local_rc, $rows, $cols);
}   # end calc_columns


#--------------------------------------------------------------------#
# create_display:                                                    #
#   Move the directory list to an array that is parsed for display   #
#                                                                    #
# Parmaeters:                                                        #
#   $dir_ref - reference to the directory - used for the count       #
#   $dir_name- name of the directory being displayed                 #
#   $rel_path - denotes whether a relative path is being displayed   #
#   $dir_list - the directory list as given from the extension       #
#                                                                    #
# Returns:                                                           #
#   $local_rc - 0 if successful, > 0 otherwise.                      #
#                                                                    #
# Globals Used:                                                      #
#   $Opt_Recursive, $Opt_Columns, $Opt_Delimiter - referenced if set #
#--------------------------------------------------------------------#
sub create_display 
{
my ($dir_ref, $dir_name, $rel_path, $dir_list) = @_;

# Set up local variables
my($num_entries, $name, $i);
my $index = 0;
my $local_rc = 0;
my $warning_rc = 0;
my @dir_array = ();
my $label = $FALSE;
my $path = "";
my $type;

# If more than one directory than display this directory name
# followed by a : then the directory list.

if ($#$dir_ref > 0) {           # remember empty @directory is -1
    # Print the leading directory name with an extra separating
    # line
    print "\n$dir_name:\n";
}

$num_entries = $dir_list->get_array_count;
for ($i = 0; $i < $num_entries; $i++) {
    $name = $dir_list->getName($i);
    $type = $dir_list->getType($i);

    # Only have directory containers when recursive flag
    # is on.  Print directory name: versus the contents
    # of the directory.

    if ($type == SR_DIRECTORY_CONTAINER) {

        # Assign the name to the dir_array for later printing
        # If using a relative path, chop off the leading
        # path name up to the level below the current working
        # directory ($dir_name)
        
        # Add a the trailing ':' to mark it's a container

        if (($rel_path) and ($CT_SR_HOME))  {
            # Strip off the leading part of the path including
            # the current working directory, then add './' & ':'

            $name =~ s/^.*$dir_name\/(.*)$/$1/; 
            $dir_array[$index++] = './'.$name.':';
            
        }
        elsif ($dir_name eq '/') {
            # Add './' to the front
            $dir_array[$index++] = '.'.$name.':';
        }
        elsif ($rel_path) {
            # Strip off any leading part of the path and add ':'
            # Keep the given directory name in the path, however.
            $name =~ s/^.*\/($dir_name\/.*)$/$1/; 
            $dir_array[$index++] = $name.':';
        }
        elsif ($dir_name eq '/') {
            # Add './' to the front
            $dir_array[$index++] = '.'.$name.':';
        }
        else {
            $dir_array[$index++] = $name.':';
        }

        # Store this path name in order to trim each entry
        $path = $name;

    }
    else {
        # Strip off the leading path to display only
        # the entry name. Store the entry name for later printing

        $name =~ s/^(.*)\/(.*)$/$2/;

        if ($Opt_Append_Type) { 
            ($warning_rc, $name) = 
                append_type($name, $dir_list->getType($i));
        }

        $dir_array[$index++] = $name;
    }
}

# Assumption: Have the entire list of a directory (possibly
# recursive) to list in @dir_array

if ($Opt_Delimiter) {

    foreach(@dir_array) {
        if ($_ =~ /:/) {
            # Is a directory label.
            if (!$label) { print "\n";}
            print "\n", $_,"\n";
            $label = $TRUE;
        }
        else { 
            $label = $FALSE;
            print "$_$Opt_Delimiter"; 
        }
        
    }

}
elsif ($Opt_Columns) {
    
    # TODO: need to figure out if it's coming from a terminal or not.
    # sheesh! this is getting complicated!

    my ($rows, $cols, $columns);
    # Grab the column width - This might go in a sub
    if ($COLUMNS) {
        $columns = $COLUMNS;
    }
    else {
        $columns = `/usr/bin/termdef -c`;
        if ($? > 0) { $columns = 80; }  # default to 80 if an error
    }


    if ($Opt_Recursive) {

        if ((($rel_path) && ($CT_SR_HOME)) or ($dir_name eq '/'))  {
            $local_rc = print_recursive_columns(".", @dir_array);
        }
        else {
            $local_rc = print_recursive_columns($dir_name, @dir_array);
        }

    }
    else {

        $local_rc = print_columns(@dir_array);

    } # end if $Opt_Recursive


}
else {
    # Format a single column list
    # This covers the -l option by default.

    foreach (@dir_array) {

        if ($_ =~ /:/) {
            # Is a directory label
            print "\n", $_, "\n";
        }           
        else { print $_,"\n"; }
    }
}   
if ($warning_rc > $local_rc) { return $warning_rc; }
else { return $local_rc; }

}   # end create_display


#--------------------------------------------------------------------#
# print_recursive_columns                                            #
#   Recursive subroutine that prints a column list from an array.    #
#   Expected format is set up by create_display, where directory     #
#   containers are ended with ':', and entries in a directory have   #
#   been stripped of all but the necessary path name.                #
#   Uses print_columns to print each directory list before recursing #
#                                                                    #
# Parameters:                                                        #
#   $branch - the branch of the directory tree being printed         #
#   @ar - array of entries belonging to the branch                   #
#                                                                    #
# Return values:                                                     #
#   $local_rc       - 0 : success  > 0 otherwise                     #
#--------------------------------------------------------------------#
sub print_recursive_columns 
{
# Intended to take care of the case -RC
my ($branch, @ar) = @_;

my @temp_array = ();
my @sub_array = ();
my @root_dir = ();
my @output_array = ();
my $local_rc = 0;
my $dir = "";

@temp_array = @ar;

# Grab the first group of entries up to the first directory
# listing
$dir = shift @temp_array;

while (@temp_array && ($dir !~ /:/)) {
    push @root_dir, $dir;
    $dir = shift @temp_array;
}

# This will stop recursing when no entries are found under a directory
if ($#root_dir == (-1)) { 
    if ($dir ne "") { print $dir,"\n";}
    print "\n";
    return(0); 
}

# Don't lose the last entry if @temp_array is emptied
if (($#temp_array == -1)and($dir !~ /:/)) { push @root_dir, $dir; }
else { unshift @temp_array, $dir; }

@root_dir = sort(@root_dir);
$local_rc = print_columns(@root_dir);

# This should print a final eol after the individual directory list
print "\n";

# If @temp_array is emptied, then there are no more
# directories to process in this batch, so exit.
if ($#temp_array == -1) { return (0); }

# Next print each of the directories contained

while(@root_dir) {
    $next_dir = shift @root_dir;
    # Remove trailing slashes that denote directory entries
    if ($next_dir =~ /^.*\/$/) { chop $next_dir; }

    # Need to locate that directory in the array list. Here
    # is the extra step of scanning through the array
    # to find the starting point of the listing for that array.

    my $ind = 0;


    # Scan the directory list for the $next_dir header entry
    $dir = $temp_array[$ind];
    $ind++;
    while ($ind <= ($#temp_array+1)) {

        # This is not as elegant as it could be. It's trying to
        # deal with all the situations - relative path, abs
        # path and root path..
        if ( (($branch eq '/') and ( $dir =~ /^\/$next_dir:/)) or
         (($branch eq '') and ( $dir =~ /^$next_dir:/)) or
         ($dir =~ /^$branch\/$next_dir:/) ) { 

            print "$dir\n";
            #if ($#temp_array == 0) { print "\n"; }
            last;
        }
        $dir = $temp_array[$ind];
        $ind++;
    }
    # Should at this point have the starting position of the
    # next array to be displayed
        
    # Set up the new branch path to pass in and check for
    my $new_branch;
    if ($branch eq "/") { $new_branch = '/'.$next_dir; }
    elsif ($branch eq "") { $new_branch = $next_dir; }
    else { $new_branch = $branch.'/'.$next_dir; }

    $Verbose && print "new branch: $new_branch\n";

    # Need to take an array slice here that contains all the subdirs
    # of the current $next_dir. 

    my $start_ind = $ind-1;
    while ($ind <= $#temp_array) {
        $dir = $temp_array[$ind];
        if (($dir =~ /:/) && ($dir !~ /^$new_branch/)) { last; }
        $ind++;
    }

    # If the previous while loop ends with $ind == $#temp_array, then
    # you know there was not a match, so go on to the next entry.
    if ($ind == $#temp_array+2) { next; }   

    # Otherwise, continue on to process the next directory
    my $end_ind = $ind;

    my $count = $end_ind - $start_ind;
    ($count > 0) ? @sub_array = 
                    splice(@temp_array, $start_ind, $count) :
                return (0);

    $Verbose && print @sub_array,"\n";

    # Get rid of the directory listing at the top of the array
    $dir = shift @sub_array;

    # Continue printing for the subdirectory
    if ($#sub_array != -1) {
    $local_rc = print_recursive_columns($new_branch, @sub_array) 
    }
    else { print "\n"; }    # print a space at the end of a branch.

} # end while   

return $local_rc;   
}   # end print_recursive_columns


#--------------------------------------------------------------------#
# print_columns                                                      #
#   Prints entries from an array. Uses calc_columns and              #
#   calc_column_widths to determine the number of columns and their  #
#   widths.                                                          #
#   Print order is a bit strange, but follow ls pattern of being     #
#   alphabetical down each column.                                   #
#                                                                    #
# Parameters:                                                        #
#   @display_list - list of entries to be printed in columns         #
#                                                                    #
# Return values:                                                     #
#   $local_rc       - 0 : success  > 0 otherwise                     #
#--------------------------------------------------------------------#
sub print_columns 
{
# Intended to handle the -C option only

my @display_list = @_;

my $local_rc = 0;
my $rows = 0;
my $cols = 0;


# Sort the array (assume is not recursive.)
@display_list = sort(@display_list);

($local_rc, $rows, $cols) =
    calc_columns($#display_list+1, 1, $cols, 0, @display_list);
$local_rc = 0;

my @col_widths = calc_col_widths($rows, $cols, @display_list);

# Print each row out, but with the info sorted down the columns
# - ls doesn't bother to truncate the column names less
# the unix supported length - it simply wraps the entries, so this
# code does the same.

for ($i = 0; $i < $rows; $i++) {
    for ($j = 0; $j<$cols; $j++) {
        $tmp2 = $col_widths[$j];

        $tmp = sprintf "%-${tmp2}s", ($display_list[$i + ($rows*$j)]);
        $tmp = ($display_list[$i + ($rows*$j)]);

        print $tmp, " " x ($tmp2 - length($tmp));

        if ($j < ($cols-1) ) { print "   "; }
    }
    print "\n";
}

return $local_rc;
}   # end print_columns


#--------------------------------------------------------------------#
# append_type - Append a "/", "?", or nothing to the end of the      #
#   entry name based on the type.                                    #
#   / for directory, ? for unknown, etc.                             #
#                                                                    #
# Paramaters:                                                        #
#   $name  input  Entry name: table name or a directory name.        #
#   $type  input  Entry type: table, directory or unknown type.      #
#                                                                    #
# Return:                                                            #
#   $name - entry name postfixed with a "/", "?", etc as appropriate.#
#--------------------------------------------------------------------#
sub append_type
{
my($name, $type) = @_;                    

my $local_rc = 0;

use CT::SR qw(:sr_entry_t);              # ENUM for Entry Types

if ($type == SR_DIRECTORY) {             # directories get a "/"
    $name = $name . "\/"; 
}
elsif ($type == SR_UNKNOWN_ENTRY) {      # unknowns get a "?"
    $name = $name . "?"; 
    # Continue to print, but signal a registry error
    $local_rc = SR_CLI_REGISTRY_ERROR    
}                                        # tables don't get anything
return ($local_rc, $name);
}   # end append_type


#--------------------------------------------------------------------#
# print_usage : print the usage statement (syntax) to stdout.        #
#   See this command's prologue syntax section for current usage.    #
#--------------------------------------------------------------------#
sub print_usage
{
printIMsg("IMsglssrUsage");
}   # end print_usage


#--------------------------------------------------------------------#
# End File                                                           #
#--------------------------------------------------------------------#
